/*
 * Copyright (c) 2022 CTIOT Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ethernetif.h"
#include "app_ethernet.h"
#include "los_interrupt.h"
#include "los_tick.h"
#include "lwip/etharp.h"
#include "lwip/snmp.h"
#include "enc28j60.h"

#define IFNAME0 'c'
#define IFNAME1 't'

osThreadId_t gEthTaskID = NULL;
static unsigned char mysendbuf[1500];	
static unsigned char myrecvbuf[1500];


void getmac(uint8_t *pMacBuf)
{
    if (pMacBuf == NULL)
        return;

    uint32_t uiMcuId = 0;
    uint8_t pMcuID[15] = {0};
    int i = 0;
    uint32_t CpuID[3] = {0};

    CpuID[0] = *(uint32_t *)(0x1fff7a10);
    CpuID[1] = *(uint32_t *)(0x1fff7a14);
    CpuID[2] = *(uint32_t *)(0x1fff7a18);
    printf("MCU UID: %08X-%08X-%08X\r\n",CpuID[0],CpuID[1],CpuID[2]);

    pMcuID[0] = (uint8_t)(CpuID[0] & 0x000000FF);
    pMcuID[1] = (uint8_t)((CpuID[0] & 0xFF00) >> 8);
    pMcuID[2] = (uint8_t)((CpuID[0] & 0xFF0000) >> 16);
    pMcuID[3] = (uint8_t)((CpuID[0] & 0xFF000000) >> 24);

    pMcuID[4] = (uint8_t)(CpuID[1] & 0xFF);
    pMcuID[5] = (uint8_t)((CpuID[1] & 0xFF00) >> 8);
    pMcuID[6] = (uint8_t)((CpuID[1] & 0xFF0000) >> 16);
    pMcuID[7] = (uint8_t)((CpuID[1] & 0xFF000000) >> 24);

    pMcuID[8] = (uint8_t)(CpuID[2] & 0xFF);
    pMcuID[9] = (uint8_t)((CpuID[2] & 0xFF00) >> 8);
    pMcuID[10] = (uint8_t)((CpuID[2] & 0xFF0000) >> 16);
    pMcuID[11] = (uint8_t)((CpuID[2] & 0xFF000000) >> 24);

    uiMcuId = (CpuID[0] >> 1) + (CpuID[1] >> 2) + (CpuID[2] >> 3);

    for (i = 0; i < 12; i++) {
        pMcuID[12] += pMcuID[i];
    }
    for (i = 0; i < 12; i++) {
        pMcuID[13] ^= pMcuID[i];
    }

    pMacBuf[0] = pMcuID[12] & 0xFE;
    pMacBuf[1] = (uint8_t)(uiMcuId & 0xFF);
    pMacBuf[2] = (uint8_t)((uiMcuId & 0xFF00) >> 8);
    pMacBuf[3] = (uint8_t)((uiMcuId & 0xFF0000) >> 16);
    pMacBuf[4] = (uint8_t)((uiMcuId & 0xFF000000) >> 24);
    pMacBuf[5] = pMcuID[13];
    return;
}

void low_level_init(struct netif *netif)
{
    uint32_t regvalue = 0;
    uint8_t macaddress[6] = {0};
    EthLinkInfo linkInfo = {0};

    get_ethernet_link_info(&linkInfo);
    if (linkInfo.useStaticMac == 1) {
        memcpy(macaddress, linkInfo.macAddr, 6);
    } else {
        getmac(macaddress);
        memcpy(linkInfo.macAddr, macaddress, 6);
        set_ethernet_link_info(&linkInfo);
    }

    netif->hwaddr_len = ETHARP_HWADDR_LEN;
    /*netif->hwaddr[0] = macaddress[0];
    netif->hwaddr[1] = macaddress[1];
    netif->hwaddr[2] = macaddress[2];
    netif->hwaddr[3] = macaddress[3];
    netif->hwaddr[4] = macaddress[4];
    netif->hwaddr[5] = macaddress[5];*/
    netif->hwaddr[0] = 0x7C;
    netif->hwaddr[1] = 0x8A;
    netif->hwaddr[2] = 0xE1;
    netif->hwaddr[3] = 0x84;
    netif->hwaddr[4] = 0xB7;
    netif->hwaddr[5] = 0xFC;


    printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", netif->hwaddr[0], netif->hwaddr[1], netif->hwaddr[2], netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]);

    /* maximum transfer unit */
    netif->mtu = 1500;

    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

    enc28j60Init(netif->hwaddr);
    if (gEthTaskID == NULL) {
        osThreadAttr_t attr = {.name = "ETH_Input", .attr_bits = 0U, .cb_mem = NULL, .stack_mem = NULL, .stack_size = 1024 * 4, .priority = 10};
        gEthTaskID = osThreadNew((osThreadFunc_t)ethernetif_input, netif, &attr);
        if (gEthTaskID == NULL) {
            ETH_DEBUG("ETH_IPC_Create gEthTaskID failed!\r\n");
        }
    }
}

struct pbuf *low_level_input(struct netif *netif)
{
    struct ethernetif *ethernetif = netif->state;
    struct pbuf *p, *q;
    u16_t len = 0;
    u16_t i = 0;

    /* Obtain the size of the packet and put it into the "len"
     variable. */
    len = enc28j60PacketReceive(1500, myrecvbuf);
    if(len == 0)
    {
        return NULL;
    }

#if ETH_PAD_SIZE
  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif

    /* We allocate a pbuf chain of pbufs from the pool. */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  
    if (p != NULL) {
#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    for(q = p; q != NULL; q = q->next) 
		{
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable.
       * This does not necessarily have to be a memcpy, you can also preallocate
       * pbufs for a DMA-enabled MAC and after receiving truncate it to the
       * actually received size. In this case, ensure the tot_len member of the
       * pbuf is the sum of the chained pbuf len members.
       */
      //read data into(q->payload, q->len);
        memcpy(q->payload, &myrecvbuf[i], q->len);
        i += q->len;
        q = q->next;
        if(i>=len)
	    break;
    }
    //acknowledge that packet has been read();

#if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.recv);
  } else {
    //drop packet();
    LINK_STATS_INC(link.memerr);
    LINK_STATS_INC(link.drop);
  }

    return p;  
}

err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    struct ethernetif *ethernetif = netif->state;
    struct pbuf *q;
    unsigned int templen = 0;

  //initiate transfer();
  
#if ETH_PAD_SIZE
  pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
    for(q = p; q != NULL; q = q->next) 
	{
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    //send data from(q->payload, q->len);
	memcpy(&mysendbuf[templen], q->payload, q->len);
	templen += q->len;
	if((templen>1500)||(templen>p->tot_len))
	{
		printf("PacketSend:length over\r\n");
		return	ERR_BUF;
	}
    }
    enc28j60PacketSend(templen, mysendbuf); //send msg
	
#if ETH_PAD_SIZE
  pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
  
    LINK_STATS_INC(link.xmit);

    return ERR_OK;
}


void ethernetif_input(void const *argument)
{
    struct pbuf *p;
    struct netif *netif = (struct netif *)argument;
    for (;;) {
        osDelay(15);
        if(enc28j60Read(EPKTCNT)) {
            do {
                p = low_level_input(netif);
                if (p != NULL) {
                    if (netif->input(p, netif) != ERR_OK) {
			printf("ethernetif_input: IP input error\n");
                        pbuf_free(p);
			p = NULL;
                    }
                }
            } while (p != NULL);
        }
    }
}

err_t ethernetif_init(struct netif *netif)
{

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "CTIOT_ENC28J60";
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
    netif->name[0] = IFNAME0;
    netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */

    netif->output = etharp_output;

    netif->linkoutput = low_level_output;

    low_level_init(netif);

    return ERR_OK;
}

